package cn.ococci.dm.Main;

import android.util.Log;

import com.alibaba.fastjson.Json;

import java.io.File;
import java.io.FilenameFilter;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.TreeSet;

import cn.ococci.dm.Base.ActivityStack;
import cn.ococci.dm.Main.Model.CrashData;
import cn.ococci.dm.Main.Model.CurUser;
import cn.ococci.dm.Main.Tools.FileTool;
import cn.ococci.dm.Main.Tools.LogTool;
import cn.ococci.dm.Main.Tools.ToastTool;
import cn.ococci.dm.Network.HttpCallback;
import cn.ococci_dm.R;


/**
 * UncaughtExceptionHandler：线程未捕获异常控制器是用来处理未捕获异常的。
 * 如果程序出现了未捕获异常默认情况下则会出现强行关闭对话框
 * 实现该接口并注册为程序中的默认未捕获异常处理
 * 这样当未捕获异常发生时，就可以做些异常处理操作
 * 例如：收集异常信息，发送错误报告 等。
 * <p/>
 * UncaughtException处理类,当程序发生Uncaught异常的时候,由该类来接管程序,并记录发送错误报告.
 */
public class CrashHandler implements Thread.UncaughtExceptionHandler {
    /**
     * Debug Log Tag
     */
    public static final String TAG = "CrashHandler";
    private static final String VERSION_NAME = "versionName";
    private static final String VERSION_CODE = "versionCode";
    private static final List<String> saveFields = Arrays.asList("BRAND", "CPU_ABI", "FINGERPRINT", "MANUFACTURER");

    /**
     * 错误报告的文件夹
     */
    private static final String CRASH_REPORTER_DIR = "crash/";
    /**
     * 错误报告文件的扩展名
     */
    private static final String CRASH_REPORTER_EXTENSION = ".cr";


    /**
     * CrashHandler实例
     */
    private static CrashHandler INSTANCE;

    /**
     * 系统默认的UncaughtException处理类
     */
    private Thread.UncaughtExceptionHandler mDefaultHandler;

    /**
     * 保证只有一个CrashHandler实例
     */
    private CrashHandler() {
    }

    /**
     * 获取CrashHandler实例 ,单例模式
     */
    public static CrashHandler getInstance() {
        if (INSTANCE == null)
            INSTANCE = new CrashHandler();
        return INSTANCE;
    }

    /**
     * 初始化,注册Context对象, 获取系统默认的UncaughtException处理器, 设置该CrashHandler为程序的默认处理器
     */
    public void init() {
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
        Thread.setDefaultUncaughtExceptionHandler(this);
    }

    /**
     * 当UncaughtException发生时会转入该函数来处理
     */
    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        if (!handleException(thread, ex) && mDefaultHandler != null) {
            // 如果用户没有处理则让系统默认的异常处理器来处理
            Log.e(TAG, "Crash交给了默认处理类处理");
            mDefaultHandler.uncaughtException(thread, ex);
        } else {
            Log.e(TAG, "出现了崩溃异常，线程：" + thread.getName(), ex);

            if (thread.getName().equals("main")) {
                mDefaultHandler.uncaughtException(thread,ex);
            }
        }
    }

    /**
     * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成. 开发者可以根据自己的情况来自定义异常处理逻辑
     *
     * @param ex
     * @return true:如果处理了该异常信息;否则返回false
     */
    private boolean handleException(Thread thread, Throwable ex) {

        try {
            if (ex == null) {
                return true;
            }

            LogTool.log(LogTool.CYAN, "处理异常信息");

            CrashData crashData = buildCrashData(ex);
            saveCrashInfoToFile(crashData);

            if (thread.getName().equals("main")) {
                ToastTool.showOnSubThread(R.string.crash_toast);
            } else {
                // 使用Toast来显示异常信息
                ActivityStack.openCrash();
            }
            // 发送错误报告到服务器

            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    public CrashData buildCrashData(Throwable ex) {
        CrashData crashData = new CrashData();

        if (CurUser.curUser != null){

            crashData.setUserId(CurUser.getServerId());
        }else {

            crashData.setUserId(0);
        }


        crashData.setTimestamp(System.currentTimeMillis());
        Writer info = new StringWriter();
        PrintWriter printWriter = new PrintWriter(info);
        // printStackTrace(PrintWriter s)
        // 将此 throwable 及其追踪输出到指定的 PrintWriter
        ex.printStackTrace(printWriter);

        // getCause() 返回此 throwable 的 cause；如果 cause 不存在或未知，则返回 null。
        Throwable cause = ex.getCause();
        while (cause != null) {
            cause.printStackTrace(printWriter);
            cause = cause.getCause();
        }
        printWriter.close();
        // toString() 以字符串的形式返回该缓冲区的当前值。
        crashData.setErrorInfo(info.toString());
        return crashData;
    }


    /**
     * 把错误报告发送给服务器,包含新产生的和以前没发送的.
     */
    public void sendCrashReportsToServer(boolean isBack) {
        String[] crFiles = getCrashReportFiles();
        if (crFiles != null && crFiles.length > 0) {
            final TreeSet<String> sortedFiles = new TreeSet<>();
            sortedFiles.addAll(Arrays.asList(crFiles));
            final String dir = FileTool.getAppFileDir() + CRASH_REPORTER_DIR;
            List<CrashData> crashDataList=new ArrayList<>(sortedFiles.size());
            for (String fileName : sortedFiles) {
                CrashData crashData = null;
                try {
                    crashData = Json.parseObject(FileTool.getStringFromFile(new File(dir, fileName)), CrashData.class);
                } catch (Exception e) {
                }
                crashDataList.add(crashData);
            }

            UserBiz.uploadErrorLog(isBack, crashDataList, new HttpCallback() {
                @Override
                public void onSuccess(Object data) {
                    for (String fileName : sortedFiles) {
                        File cr = new File(dir, fileName);
                        cr.delete();
                    }

                }
            });

        }
    }



    /**
     * 保存错误信息到文件中
     *
     * @return
     */
    public void saveCrashInfoToFile(CrashData crashData) {

    	String jsonString=null;
        try {
            makeDir();
            final String filename = FileTool.getAppFileDir() + CRASH_REPORTER_DIR + "crash-"+ CRASH_REPORTER_EXTENSION;
            // 保存文件
            jsonString=  crashData.toString();
            FileTool.writerStringToFile(jsonString, filename);
        } catch (Exception e) {
            Log.e(TAG, "保存文件时出错:" + jsonString, e);
        }
    }

    private void makeDir() {
        File file = new File(FileTool.getAppFileDir() + CRASH_REPORTER_DIR);
        if (!file.exists()) {
            file.mkdirs();
        }
    }


    /**
     * 获取错误报告文件名
     *
     * @return
     */
    private String[] getCrashReportFiles() {
        File filesDir = new File(FileTool.getAppFileDir() + CRASH_REPORTER_DIR);
        if (!filesDir.exists())
            return null;
        // 实现FilenameFilter接口的类实例可用于过滤器文件名
        FilenameFilter filter = new FilenameFilter() {
            // accept(File dir, String name)
            // 测试指定文件是否应该包含在某一文件列表中。
            public boolean accept(File dir, String name) {
                return name.endsWith(CRASH_REPORTER_EXTENSION);
            }
        };
        // list(FilenameFilter filter)
        // 返回一个字符串数组，这些字符串指定此抽象路径名表示的目录中满足指定过滤器的文件和目录
        return filesDir.list(filter);
    }





}
